import {
  Box3,
  BoxGeometry,
  BufferGeometry,
  Camera,
  Line,
  LineBasicMaterial,
  Mesh,
  MeshBasicMaterial,
  Object3D,
  Raycaster,
  Vector3,
} from 'three'

type HandlerType = 'X' | 'Y' | 'Z'

// 拖动操作触点
const gizmoMaterial = new MeshBasicMaterial({
  depthTest: false,
  depthWrite: false,
  fog: false,
  toneMapped: false,
  transparent: true,
})
const matRed = gizmoMaterial.clone()
matRed.color.setHex(0xff0000)
const matGreen = gizmoMaterial.clone()
matGreen.color.setHex(0x00ff00)
const matBlue = gizmoMaterial.clone()
matBlue.color.setHex(0x0000ff)
const handlerMaterials: Record<
  HandlerType,
  [MeshBasicMaterial, MeshBasicMaterial]
> = {
  X: [matRed, matRed],
  Y: [matGreen, matGreen],
  Z: [matBlue, matBlue],
}

const gizmoLineMaterial = new LineBasicMaterial({
  depthTest: false,
  depthWrite: false,
  fog: false,
  toneMapped: false,
  transparent: true,
})
const gizmoLineRed = gizmoLineMaterial.clone()
gizmoLineRed.color.setHex(0xff0000)
const gizmoLineGreen = gizmoLineMaterial.clone()
gizmoLineGreen.color.setHex(0x00ff00)
const gizmoLineBlue = gizmoLineMaterial.clone()
gizmoLineBlue.color.setHex(0x0000ff)

const gizmoHelpers: Record<
  HandlerType,
  [LineBasicMaterial, LineBasicMaterial]
> = {
  X: [gizmoLineRed, gizmoLineRed],
  Y: [gizmoLineGreen, gizmoLineGreen],
  Z: [gizmoLineBlue, gizmoLineBlue],
}
class MyTransform {
  public root: Mesh = new Mesh()
  public catchCamera?: Camera
  public catchElement?: HTMLElement
  public handlers: Record<HandlerType, [Vector3, Vector3]> = {
    X: [new Vector3(1, 0, 0), new Vector3(-1, 0, 0)],
    Y: [new Vector3(0, 1, 0), new Vector3(0, -1, 0)],
    Z: [new Vector3(0, 0, 1), new Vector3(0, 0, -1)],
  }

  public handlersMesh: Mesh[] = []
  public helperLines: Line[] = []

  public object?: Object3D

  public catchLayers: number[] = []

  public showX: boolean = true
  public showY: boolean = true
  public showZ: boolean = true
  public _raycaster = new Raycaster()

  public isVisible: boolean = false

  constructor(camera: Camera, element: HTMLElement, layers: number[]) {
    this.catchCamera = camera
    this.catchElement = element
    this.catchLayers = layers
    this.initDragHandler()
    return this
  }

  public initEvent() {
    this.catchElement?.addEventListener('pointerdown', this.onPointerDown)
    this.catchElement?.addEventListener('pointermove', this.onPointerMove)
    this.catchElement?.addEventListener('pointerup', this.onPointerUp)
  }

  public onPointerDown() {}
  public onPointerMove() {}
  public onPointerUp() {}

  public initDragHandler() {
    const size = 0.08
    const objPosition = new Vector3(0, 0, 0)
    const objectSize = new Vector3(1, 1, 1)
    for (const i in this.handlers) {
      this.handlers[i].forEach((element: Vector3, index) => {
        const mesh = new Mesh(
          new BoxGeometry(size, size, size),
          handlerMaterials[i][index],
        )
        mesh.name = i
        const position = new Vector3().copy(element)
        position.multiply(objectSize).divideScalar(2)
        mesh.position.copy(position)
        this.handlersMesh.push(mesh)
        this.root.add(mesh)
      })
      const points: Vector3[] = []
      points.push(objPosition.clone())
      points.push(
        objPosition.clone().add(this.handlers[i][0].multiply(objectSize)),
      )
      const geometry = new BufferGeometry().setFromPoints(points)
      const line = new Line(geometry, gizmoHelpers[i][0])
      line.name = i
      this.helperLines.push(line)
      this.root.add(line)
    }
    this.updateVisible()
    // const objectSize = this.getObjectSize()

    // const minSize = Math.min(objectSize.x, objectSize.y, objectSize.z)
    // const size = minSize / 10
    // for (const i in this.handlers) {
    //   this.handlers[i].forEach((element: Vector3, index) => {
    //     const mesh = new Mesh(
    //       new BoxGeometry(size, size, size),
    //       handlerMaterials[i][index],
    //     )
    //     mesh.name = i
    //     const position = element.multiply(objectSize).divideScalar(2)
    //     mesh.position.copy(position)
    //     this.handlersMesh.push(mesh)
    //     this.root.add(mesh)
    //   })
    //   const points: Vector3[] = []
    //   points.push(this.object!.position.clone())
    //   points.push(
    //     this.object!.position.clone().add(
    //       this.handlers[i][0].multiply(objectSize),
    //     ),
    //   )
    //   const geometry = new BufferGeometry().setFromPoints(points)
    //   const line = new Line(geometry, gizmoHelpers[i][0])
    //   line.name = i
    //   this.helperLines.push(line)
    //   this.root.add(line)
    // }
  }

  public updateVisible = () => {
    this.root.visible = this.isVisible
  }

  public updateHandler = () => {
    const objectSize = this.getObjectSize()
    // const minSize = Math.min(objectSize.x, objectSize.y, objectSize.z)
    // const size = minSize / 10
    for (const i in this.handlers) {
      const handlerIndex = Object.keys(this.handlers).findIndex((e) => e === i)
      this.handlers[i].forEach((element: Vector3, index) => {
        const i = handlerIndex * 2 + index
        const mesh = this.handlersMesh[i]
        const position = new Vector3().copy(element)
        position.multiply(objectSize).divideScalar(2)
        mesh.position.copy(position)
        this.handlersMesh.push(mesh)
        this.root.add(mesh)
      })
      // const points: Vector3[] = []
      // points.push(this.object!.position.clone())
      // points.push(
      //   this.object!.position.clone().add(
      //     this.handlers[i][0].multiply(objectSize),
      //   ),
      // )
      // const geometry = new BufferGeometry().setFromPoints(points)
      // const index = Object.keys(this.handlers).findIndex((e) => e === i)
      // const line = this.helperLines[index]
      // line.geometry = geometry
      // const line = new Line(geometry, gizmoHelpers[i][0])
      //   line.name = i
      //   this.helperLines.push(line)
      //   this.root.add(line)
    }
  }

  public attach(obj: Object3D) {
    this.object = obj
    this.updateShowHandler()
    this.updateHandler()

    this.catchLayers.forEach((layer) => {
      this.root.layers.set(layer)
      this.root.traverse((child) => {
        child.layers.set(layer)
      })
    })
    this.isVisible = true
    this.updateVisible()
  }

  public getObjectSize = () => {
    const size = new Vector3(1, 1, 1)
    if (!this.object) return size
    const bbox = new Box3().setFromObject(this.object)
    bbox.getSize(size)
    return size
  }

  public getObjectCenter = () => {
    const center = new Vector3()
    if (!this.object) return center
    const bbox = new Box3().setFromObject(this.object)
    bbox.getCenter(center)
    return center
  }

  public setShowHandler = (data: {
    showX: boolean
    showY: boolean
    showZ: boolean
  }) => {
    this.showX = data.showX
    this.showY = data.showY
    this.showZ = data.showZ
  }

  public updateShowHandler = () => {
    this.handlersMesh
      .filter((e) => e.name === 'X')
      .forEach((e) => (e.visible = this.showX))
    this.handlersMesh
      .filter((e) => e.name === 'Y')
      .forEach((e) => (e.visible = this.showY))
    this.handlersMesh
      .filter((e) => e.name === 'Z')
      .forEach((e) => (e.visible = this.showZ))

    this.helperLines
      .filter((e) => e.name === 'X')
      .forEach((e) => (e.visible = this.showX))
    this.helperLines
      .filter((e) => e.name === 'Y')
      .forEach((e) => (e.visible = this.showY))
    this.helperLines
      .filter((e) => e.name === 'Z')
      .forEach((e) => (e.visible = this.showZ))

    console.log(this.handlersMesh.map((e) => e.visible))
  }
}

export default MyTransform
